% Назначение: Декодер TCM
% Вход: point      - точка сигнального пространства (может быть зашумленной)
%       trel       - структура описывающая таблицу переходов используемого
%                    свёрточного кода
%       signals    - множество сигнальных точек модуляции
%       tblen      - глубина просмотра решетки при декодировании
%       initmetric - предшествующие метрики состояний
%       initstates - предшествующие трассы выживших путей
%       initinputs - предшествующая последовательность входных символов
%                    соответствующая трассам выживших путей
% Выход: decoded - декодированные символы
%        metric  - текущие метрики состояний
%        states  - текущие трассы выживших путей
%        inputs  - текущая последовательность входных символов
%                  соответствующая трассам выживших путей
% Обращение: [decoded, metric, states, inputs] = tcmDecoder(point, trel,...
%                       signals, tblen, initmetric, initstates, initinputs)
function [decoded, metric, states, inputs] = tcmDecoder(point, trel,...
                        signals, tblen, initmetric, initstates, initinputs)
if isempty(point) % сигнальная точка пуста
    decoded = [];
    metric  = [];
    states  = [];
    inputs  = [];
else
    if isempty(initmetric) && isempty(initstates) &&...
       isempty(initinputs) % начальное состояние
        % инициализация метрик для состояний
        oldMetric = Inf * ones(1, trel.numStates); % старые метрики
        
        % инициализация трассы выживших путей
        states = zeros(trel.numStates, tblen);
        
        % инициализация последовательности входных символов соответствующих
        % трассам выживших путей
        inputs = zeros(trel.numStates, tblen);
        
        % инициализация возможных состояний
        permittedStates = 1;
    elseif ~isempty(initmetric) && ~isempty(initstates) &&...
           ~isempty(initinputs)
        % инициализация метрик для состояний
        oldMetric = initmetric;
        
        % инициализация трассы выживших путей
        states = initstates;
        
        % инициализация последовательности входных символов соответствующих
        % трассам выживших путей
        inputs = initinputs;
        
        % инициализация возможных состояний
        permittedStates = find(initmetric < Inf); % игнорировать состояния
                                                  % с бесконечной метрикой
    else % не корректные входные параметры
        disp(['When using [] as the default for INIT_STATES, ',...
             'INIT_METRIC and INIT_INPUTS must also be []']);
    end
    
    metric = Inf * ones(1, trel.numStates); % новые метрики
    
    states  = circshift(states, [0 -1]); % циклически сдвинуть элементы 
                                         % строк матрицы
    inputs  = circshift(inputs, [0 -1]); % циклически сдвинуть элементы
                                         % строк матрицы                                        
% ??????????????? строчка кажется подозрительной, возможена ошибка
    decoded = inputs(:, tblen);          % претенденты декодирования
    
    for s = permittedStates
        % перевести все номера сигналов состояния s в сигнальные точки
        tmp = signals( :, trel.outputs(s, :) + 1 );
        for i = 1 : trel.numInputSymbols
            % мера рассогласования входной точки с возможными
            % для состояния s
%             d = norm( tmp(:, i) - point, 2 ); % Евклидова метрика
            d = sum( (tmp(:, i) - point).^2 ); % квадрат Евклидовой метрики
            
            if oldMetric( trel.nextStates(s, i) + 1 ) ~= Inf
                % если метрика не актуальна
                if d + oldMetric(s) < metric( trel.nextStates(s, i) + 1 )
                    % обновить метрику состояния
                    metric( trel.nextStates(s, i) + 1 ) = d + oldMetric(s);
                    
                    % обновить трассу выживших путей состояния
                    states( trel.nextStates(s, i) + 1, tblen ) = s - 1;
                    
                    % обновить значения входных символов для трассы
                    % выживших путей состояния
                    inputs( trel.nextStates(s, i) + 1, tblen ) = i - 1;
                end
            % если метрика не актуальна
            elseif d < metric( trel.nextStates(s, i) + 1 )
                % обновить метрику состояния
                metric( trel.nextStates(s, i) + 1 ) = d;
                
                % обновить трассу выживших путей состояния
                states( trel.nextStates(s, i) + 1, tblen ) = s - 1;
                
                % обновить значения входных символов для трассы
                % выживших путей состояния
                inputs( trel.nextStates(s, i) + 1, tblen ) = i - 1;
            end
        end
    end
    
    [~, s]  = min(metric); % наименьшая метрика и
                           % соответствующее ей состояние
    
    % ??????????? сильно сомневаюсь в этом коде
    % наверное можно обойтись и без этого цикла
    % поиск начала пути для состояния с наименьшей метрикой
    for i = tblen : -1 : 1
        s = states(s, i) + 1;
    end
    
    k       = log2( trel.numInputSymbols ); % число бит информации
    decoded = de2bi(decoded(s), k, 'left-msb')';
end

end

% trel       - структура описывающая таблицу переходов используемого свёрточного кода
% numInputSymbols	Скаляр
% Число возможных входных символов: 2^k
% numOutputsymbols	Скаляр
% Число возможных выходных символов: 2^n
% numStates         Скаляр
% Число внутренних состояний кодера
% nextStates        Матрица: numStates строк, 2^k столбцов
% Таблица переходов, указывающая состояния кодера, в которые осуществляется
% переход, для всех комбинаций текущего состояния и входного символа
% outputs           Матрица: numStates строк, 2^k столбцов
% Таблица формирования выходного сигнала, содержащая значения выходных
% символов для всех комбинаций текущего состояния и входного символа